--waf core lib
require 'config'


--[[

在处理HTTP请求时，确定发起请求的客户端的真实IP地址是非常重要的，特别是在需要进行IP过滤、地理位置分析或日志记录等操作时。 

X-Real-IP:

含义: X-Real-IP是一个非标准的HTTP头部，通常由反向代理（如Nginx或负载均衡器）设置，用于记录原始请求的客户端IP地址。

使用场景: 当OpenResty/Nginx位于反向代理后面时，直接从remote_addr获取的IP可能是代理服务器的IP而不是客户端的真实IP。
在这种情况下，反向代理可以在转发请求时附加X-Real-IP头部，指明最初的客户端IP。

区别: 相比于其他方法，X-Real-IP直接提供了反向代理认为的客户端IP，通常被认为是可靠的来源。


X-Forwarded-For (XFF):

含义: X-Forwarded-For也是一个非标准的HTTP头部，用于识别通过HTTP代理或负载均衡器发出的请求的原始IP地址。
与X-Real-IP不同，X-Forwarded-For可以包含一系列IP地址，包括客户端IP和所有中间代理的IP。

使用场景: 在多层代理的环境中使用，例如客户端通过多个代理服务器连接到最终的服务器。

区别: X-Forwarded-For可能包含多个IP地址，所以需要额外的逻辑来解析出最初的客户端IP。
同时，因为它可以被任何经过的代理服务器修改，所以可能比X-Real-IP更容易受到伪造。



ngx.var.remote_addr:

含义: ngx.var.remote_addr是Nginx内置的一个变量，用于获取直接连接到Nginx的客户端的IP地址。

使用场景: 当没有任何代理直接连接到Nginx服务器时，这个变量包含了客户端的真实IP地址。

区别: 在没有代理的情况下，这是获取客户端IP的直接和可靠方法。但如果请求是通过代理转发的，这个变量将是代理服务器的IP地址，
而不是客户端的真实IP。
]]--


--Get the client IP
function get_client_ip()
    CLIENT_IP = ngx.req.get_headers()["X_real_ip"]
    if CLIENT_IP == nil then
        CLIENT_IP = ngx.req.get_headers()["X_Forwarded_For"]
    end
    if CLIENT_IP == nil then
        CLIENT_IP  = ngx.var.remote_addr
    end
    if CLIENT_IP == nil then
        CLIENT_IP  = "unknown"
    end
    return CLIENT_IP
end

--Get the client user agent
function get_user_agent()
    USER_AGENT = ngx.var.http_user_agent
    if USER_AGENT == nil then
       USER_AGENT = "unknown"
    end
    return USER_AGENT
end

--Get WAF rule
function get_rule(rulefilename)
    local io = require 'io'
    local RULE_PATH = config_rule_dir
    local RULE_FILE = io.open(RULE_PATH..'/'..rulefilename,"r")
    if RULE_FILE == nil then
        return
    end
    RULE_TABLE = {}
    for line in RULE_FILE:lines() do
        table.insert(RULE_TABLE,line)
    end
    RULE_FILE:close()
    return(RULE_TABLE)
end

--WAF log record for json,(use logstash codec => json)
function log_record(method,url,data,ruletag)
     -- 引入cjson库来处理JSON
    local cjson = require("cjson")
     -- 引入io库来处理文件I/O
    local io = require 'io'
    local LOG_PATH = config_log_dir
    local CLIENT_IP = get_client_ip()
    local USER_AGENT = get_user_agent()
    local SERVER_NAME = ngx.var.server_name
    local LOCAL_TIME = ngx.localtime()
    local log_json_obj = {
                 client_ip = CLIENT_IP,
                 local_time = LOCAL_TIME,
                 server_name = SERVER_NAME,
                 user_agent = USER_AGENT,
                 attack_method = method,
                 req_url = url,
                 req_data = data,
                 rule_tag = ruletag,
              }

    -- 将JSON对象编码为字符串
    local LOG_LINE = cjson.encode(log_json_obj)
    -- 构建日志文件的完整路径，文件名包含日期
    local LOG_NAME = LOG_PATH..'/'..ngx.today().."_waf.log"

    -- 打开日志文件准备写入
    local file = io.open(LOG_NAME,"a")
    if file == nil then
        return
    end
     -- 将日志写入文件
    file:write(LOG_LINE.."\n")
    file:flush()
    file:close()
end

--WAF return
function waf_output()
    -- 检查配置中是否指定了重定向作为WAF的响应方式
    if config_waf_output == "redirect" then
        -- 如果是重定向，则将客户端重定向到指定的URL
        ngx.redirect(config_waf_redirect_url, 301)
    else
        -- 如果不是重定向，则设置响应的内容类型为"text/html"
        ngx.header.content_type = "text/html"
        -- 设置HTTP状态码为403 Forbidden
        ngx.status = ngx.HTTP_FORBIDDEN

        -- 输出在配置中定义的HTML内容
        ngx.say(config_output_html)

        -- 发送HTTP响应并结束请求处理
        ngx.exit(ngx.status)
    end
end

